/*

 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *

hipdmxctl.c - (c) 2012 - Rowan Maclachlan (hippy - dmxout@gmail.com).

Referenced material:

ANSI E1.20 2010
ANSI E1.11 2004
*/


#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <pthread.h>
#include <limits.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include "opt.h"
#include "xuart-utils.h"
#include "hipnet.h"
#include "hipdmxctl.h"


// log to a file 
// TODO: fix!
int log_setup(void){
  if((logfile=freopen("/tmp/hipdmxctl.log", "w",stdout))==NULL) {
    printf("Cannot open log file!\n");
    return -1;
  }
    printf("Log File Opened!\n");
  return 1;
}


#ifdef TX_PRIO_RT   // ---- Scheduling control optional
// we use this to control the priority of xuartctl
// find the max priority, get the current, set scheduler to round robin
void init_sched(int pid){
   sched_pmin = sched_get_priority_min(SCHED_RR);
   if (sched_pmin == -1) 
    printf("get priority min failure: %s\n",strerror(errno));
   sched_pmax = sched_get_priority_max(SCHED_RR);
   if (sched_pmax == -1) 
    printf("get priority max failure: %s\n",strerror(errno));
   sched_sp.sched_priority = sched_pmax; 
   if (sched_getparam(pid, &sched_initial_sp) != 0) 
    printf("get priority failure: %s\n",strerror(errno));
   sched_initial_policy = sched_getscheduler(pid);
   if (sched_initial_policy == -1)  
    printf("get scheduler policy failure: %s\n",strerror(errno));
 }

void raise_RT_priority(int pid)
{
  if (sched_setscheduler(pid, SCHED_RR, &sched_sp) != 0) 
   printf("set RR scheduler policy failure: %s\n",strerror(errno));
}

void drop_RT_priority(int pid)
{ 
  if (sched_setscheduler(pid, sched_initial_policy, &sched_initial_sp) != 0) 
   printf("drop RR scheduler policy failure: %s\n",strerror(errno));
}
#endif  //  TX_PRIO_RT





// ============================================
// ============================================
// ============= Reception Engine  ============
// ============================================
// ============================================

#ifdef DEBUG 
 // debugging 
static inline void decode_ch(uint16_t data) {
  static int last_channel = -1;
  
  if (ch_uart(data) != last_channel) {
    last_channel = ch_uart(data);
    printf("\n(UART:%02d): ",ch_uart(data));
  }
  switch (ch_type(data)) {
  case XUART_DATA:
    printf("(DATA:%d)",ch_dat(data));
    break;
  case XUART_BREAK:
    printf("(BREAK:%duS)",ch_time(data));
    fflush(stdout);
    break;
  case XUART_IDLE:
    printf("(IDLE:%duS)",ch_time(data));
    fflush(stdout);
    break;
  default:
    printf("(ERR)");
    break;
  }
}
#endif  // debug
 
// try to fill a buffer with whatever is available
// then returns one value from buffer if possible,
// moving the buffer position forward to the next value
// until empty buffer, then we read more.
uint16_t  read_xuart(int xuart) {
  static uint16_t   dmxbuf[XLR_LAST+1][1024];
  static int dmxq[XLR_LAST+1];
  static int dmxh[XLR_LAST+1];
  static int init = 0;
  static int i;
  static int rx_cnt[XLR_LAST+1];
  
  // initialize static vars - once only
  if (init != 1) {
    for (init = 0; init < XLR_LAST+1 ; init++){
         dmxq[init] = dmxh[init] = rx_cnt[init] = 0;
         for (i = 0; i < 1024 ; i++)
             dmxbuf[init][i] = 0;   
    }
    init=1;
  }

  // if the queue is empty, or all values have 
  // been read - try to fill it up again
  if (dmxq[xuart] == 0 || (dmxh[xuart] == dmxq[xuart])) {
    rx_cnt[xuart] = read(xlr[xuart].fd,&dmxbuf[xuart],2048);

    if (rx_cnt[xuart] <= 0) {
        return 0; // nothing read
    } else {
#ifdef DEBUG
     if (opt_debugx) printf("X-read %d bytes: ",rx_cnt[xuart]); 
#endif
    
     // overflow or weirdness occured
     if (rx_cnt[xuart]%2 != 0) { // odd number of bytes
       dmxq[xuart] = dmxh[xuart] = 0;
#ifdef DEBUG
       if (opt_debug) printf("\nRX error detected! RX buffer discarded.\n"); 
#endif       
       return -1;
     }
     dmxq[xuart] = rx_cnt[xuart]/2;  // new queue length (1x16-bit value == 2x8-bit)  
     dmxh[xuart] = 0; // position to the head of the new queue
    } 
  }
#ifdef DEBUG  
  if (opt_debugx) {
     decode_ch(dmxbuf[xuart][dmxh[xuart]]);
     printf("buf[%d] pos[%d] \n", xuart, dmxh[xuart] );
  }
#endif
  
  int q = dmxh[xuart]++;
  
  if (dmxh[xuart] == rx_cnt[xuart]) {
     dmxq[xuart] = dmxh[xuart] = 0;
  }
  return dmxbuf[xuart][q];  // return the data from the current position in the queue
                            // then increment the queue position
}



// reset and disable all rx engines
void rx_init_engines(void){
    int i, c;
    for(i=0;i<XLR_LAST+1;i++){
        rx_eng[i].state = RX_STATE_OFF;
        rx_eng[i].idle_time = rx_eng[i].break_time = rx_eng[i].data_length = 0;
        rx_eng[i].frame_time = 0;
        for(c=0;c < MAX_DMX_FRAME_SIZE;c++) rx_eng[i].data[c] = 0;
    } 
}
 
// reset (start) a specified rx engine
int rx_reset_engine(int i){
    int c;
#ifdef DEBUG     
     printf("reset engine: %d\n",i);
#endif    
    if ((i < 0) || (i > XLR_LAST)) return -1; // invalid engine number 
    rx_eng[i].state = RX_STATE_WFB;
    rx_eng[i].idle_time = rx_eng[i].break_time = rx_eng[i].data_length = 0;
    rx_eng[i].frame_time = 0;
    for(c=0;c < MAX_DMX_FRAME_SIZE+1;c++) rx_eng[i].data[c] = 0;
    return 0;
}

// stops a specified rx engine
int rx_stop_engine(int i){
    if ((i < 0) || (i > XLR_LAST)) return -1; // invalid engine number 
    rx_eng[i].state = RX_STATE_OFF;
    rx_eng[i].idle_time = rx_eng[i].break_time = rx_eng[i].data_length = 0;
    rx_eng[i].frame_time = 0;
    return 0;        
}




// feed an op_code into the engine
// activity: bit[universe number] is set to 1 if activity occured with in last second
static inline int rx_engine_feed(uint16_t rx_opcode, int *activity){
    int op_time = 0;
    int uartn = ch_uart (rx_opcode);  

#ifdef DEBUG_PROFILE   
  struct timeval fps_tim1;  
  struct timeval fps_tim2;  
  gettimeofday(&fps_tim1, NULL);
#endif 
  
      // map universes to xuart numbers
    switch (uartn){
     case 1: uartn = 0; break;
     case 2: uartn = 1; break;
     case 5: uartn = 2; break;
     case 6: uartn = 3; break;
     default: return 0; break;
    } 
  
    int op_type = ch_type(rx_opcode);  
    if ((op_type > XUART_IDLE) || (op_type < XUART_DATA)){
#ifdef DEBUG  
     printf("corrupt or invalid ch_type: %d.\n",op_type);
#endif     
     return -1; //  opcode is dropped
    }
   
    if (op_type != XUART_DATA) {  // data opcodes don't have a time payload
        op_time = ch_time (rx_opcode); // get the op_code duration time
    }

   // FSM  
    switch (rx_eng[uartn].state) {

// -------------  RX_STATE_OFF ------------
//  Engine is not running
        case RX_STATE_OFF:   // this engine is disabled
#ifdef DEBUG  
                 if (opt_debugv) printf("engine disabled...\n");
#endif         
            break;

// -------------  RX_STATE_WFB ------------
//  Engine is waiting for a break to signifiy the start of a frame
        case RX_STATE_WFB:   
            rx_eng[uartn].frame_time = 0;               
            rx_eng[uartn].data_length = 0;
            
            // check the type of opcode we are processing
            switch (op_type) {
              
              case XUART_BREAK:   // opcode was a BREAK
                 if (op_time >= RX_BREAKTIME_THRESHOLD)  {  // is it long enough?
#ifdef DEBUG  
                     if (opt_debugv) printf("NEW BREAK (%d)\n",op_time);
#endif 
                     rx_eng[uartn].frame_time = rx_eng[uartn].break_time = op_time;                    
                     rx_eng[uartn].state = RX_STATE_WFI;  // move to next state
                 }
                 break;
                               
              case XUART_IDLE:  // opcode was an IDLE
                    //  do nothing... as we are waiting for break!                    
                    break;
                    
              case XUART_DATA:  // opcode was a DATA byte
                  // possible RDM Discovery Response                  
#ifdef DEBUG  
                 if (opt_debugv) printf("x");
#endif 
                   break;  
            } // switch op_type
          break;        
        // end case WFB
      
// -------------  RX_STATE_WFI ------------
//  Engine is waiting for a MAB (mark(idle) after break) to signifiy the start of data bytes     
        case RX_STATE_WFI:    
            rx_eng[uartn].data_length = 0;  // reset rx data length

            // check the type of opcode we are processing
            switch (op_type) {
              case XUART_BREAK:
                   // Okay, so we were expecting an idle.... but we got a break!
                   // So again, since we are already waiting for an idle after a break, we 
                   // continue to wait here... but reset the frame_time to include the break we just got.
#ifdef DEBUG            
                 if (opt_debugx) printf("expected idle, got break\n");
#endif
                 rx_eng[uartn].frame_time = rx_eng[uartn].break_time = op_time;
                 break;                   
              case XUART_IDLE:
#ifdef DEBUG            
                 if (opt_debugx) printf("GOT MAB (%d)... ",op_time);
#endif
                    rx_eng[uartn].idle_time = op_time;
                    rx_eng[uartn].frame_time += rx_eng[uartn].idle_time;
                  if (rx_eng[uartn].idle_time >= RX_IDLETIME_THRESHOLD) {
#ifdef DEBUG            
                 if (opt_debugx) printf("OK!\n");
#endif
                     rx_eng[uartn].state = RX_STATE_RXDATA; // we got our MAB, next state
                     rx_eng[uartn].break_time = 0;
                   }           
                   break;        
              case XUART_DATA:
                    // do nothing - error condition
                    // TODO: this will keep waiting for next idle?
#ifdef DEBUG  
                    if (opt_debugx) printf("Error, expected IDLE and got data!\n");
#endif
                    break;
            } // switch op_type
           break;       
        // end case WFI
        
// -------------  RX_STATE_RXDATA ------------
//  We have our BREAK, and MAB, so now we are expecting DATA (or another BREAK)
        case RX_STATE_RXDATA:    
            switch (op_type) {
              // a BREAK here signals the end of this frame, and the start of the next
              case XUART_BREAK:
                      rx_eng[uartn].break_time = op_time;                      
                      rx_eng[uartn].state = RX_STATE_WRITE_DATA;   // move to next state                                                               
#ifdef DEBUG  
                    if (opt_debugx) printf("BREAK - End of frame!\n");
#endif
                    break;  
              case XUART_IDLE:
                     // idles RX'd here are inter-slot delays, just add the time                  
                     rx_eng[uartn].frame_time += op_time;
#ifdef DEBUG  
                    if (opt_debugx) 
                    printf(".");
#endif
                    break;
              case XUART_DATA:
                      rx_eng[uartn].frame_time += 11;  // time of one byte = 44uS                     
                        
                      if (rx_eng[uartn].data_length < MAX_DMX_FRAME_SIZE+1) {
                        // place the received slot into a buffer and increment the data length
                        int pos = rx_eng[uartn].data_length++;
                        rx_eng[uartn].data[pos]=ch_dat(rx_opcode);
#ifdef DEBUG  
                        if (opt_debugx) 
                        printf("*%d",pos);
#endif
                       } else {
#ifdef DEBUG  
                        printf(" - Full frame rx'd in %d uS.\n",rx_eng[uartn].frame_time);
#endif
                        rx_eng[uartn].state = RX_STATE_WRITE_DATA;   // move to next state                                              
                       }                                                        
                    break;                
           } // switch op_type
        break;        
       // end case RXDATA
                                            
        default:
            printf("\nshutdown: rx_engine[%d].state: state type %d unknown.\n", uartn, rx_eng[uartn].state);
            rx_eng[uartn].state = RX_STATE_OFF;
            return -1;// this engine is disabled, ignore this opcode
        
     } // end of top-level state switch   
     
#ifdef DEBUG_PROFILE   
  gettimeofday(&fps_tim2, NULL);
  if (opt_debugv) {
    printf("state time: %d\n",(int)fps_tim2.tv_usec - (int)fps_tim1.tv_usec);
  }
#endif      
     
     // frame rx'd, now write data to it's destination
      if (rx_eng[uartn].state == RX_STATE_WRITE_DATA){
#ifdef DEBUG                         
            if (opt_debugv) {
                 int cnt = 0;
                 printf("\nRX (%d) - break_time: %d  MAB_time: %d  data_length: %d  frame_time: %d ",uartn, rx_eng[uartn].break_time, rx_eng[uartn].idle_time, rx_eng[uartn].data_length, rx_eng[uartn].frame_time);
                 for (cnt=0;cnt<8;cnt++) printf(" %d=%u",cnt, rx_eng[uartn].data[cnt]);
                 printf("\n");                                 
            }     
#endif
                            
          // validate packet length  
          if (rx_eng[uartn].data_length <= MAX_DMX_FRAME_SIZE) {         
          
          // send the data via HipNet
           // transmit the received dmx frame to the controller over HipNet                          
           outpacket.mode = 0x01;      // dmx received from an XLR
           outpacket.srcnode = 0x01;  // we are a node, not a controller
           outpacket.universe = uartn;   // universe XLR is assigned to  
           outpacket.size = htons(rx_eng[uartn].data_length); 
//           memset(&outpacket.data,0,MAX_DMX_FRAME_SIZE);
//           memcpy(&outpacket.data, &rx_eng[uartn].data, rx_eng[uartn].data_length);
           int cnt = 0;        
            for (cnt=0;cnt < rx_eng[uartn].data_length+1; cnt++){
                outpacket.data[cnt] = rx_eng[uartn].data[cnt];
            }

#ifndef HIP_SHM
           if (hipnet_socket_write( (unsigned short *)&outpacket,9+rx_eng[uartn].data_length) < 0) {
              printf("HipNet socket write failed for XLR %d! \n",uartn); 
           }
#endif   

#ifdef HIP_SHM
           if (hipnet_shm_write(uartn, (unsigned short *)&outpacket,9+rx_eng[uartn].data_length) < 0) {
              printf("HipNet SHM write failed for XLR %d! \n",uartn); 
            }         
#endif             
         
                                  
          // if we received less than a 513 byte frame, it means we got a break in the signal
          // so if we have the break_time, we are now expecting an idle.
          // otherwise we are expecting a break to start a new frame                                  
          if (rx_eng[uartn].data_length == MAX_DMX_FRAME_SIZE) {    
               rx_eng[uartn].state = RX_STATE_WFI; // got the break already, wait for idle
               rx_eng[uartn].frame_time = rx_eng[uartn].break_time = op_time;
               return rx_eng[uartn].data_length;                                        
           } else { 
           //
               rx_eng[uartn].state = RX_STATE_WFB; //  the break, wait for it
               rx_eng[uartn].frame_time = 0;                                                     
              return rx_eng[uartn].data_length;                                        
           }   
      }
    }
      return 0;  
}




//  this thread feeds xuart opcodes to the DMX/RDM data RX engine 
void *rx_engine(void *thread_id) {
  uint16_t    rx_opcode = 0;
  static int rx_fps[XLR_LAST+1];
  int result = 0;
  int ti = 0;
  int activity = 0;
  int cnt = 0;
  int cnt2 = 0;
        
  struct timeval fps_tim1;  
  struct timeval fps_tim2;  

  gettimeofday(&fps_tim1, NULL);
 
  if (opt_debugv)
    printf("%d: RX engine started.\n",(int)fps_tim1.tv_sec);   
 
  for (;;){
    ti = 0;
    for(cnt = 0; cnt < XLR_LAST+1; cnt++){
      if (rx_eng[cnt].state != RX_STATE_OFF){

     do{
        rx_opcode = read_xuart(cnt);    // rx an opcode from the xuart, via the queue              
        if (rx_opcode) {

            ti = 1; // some data rx'd from the uart     
            // feed the op_code into the engine
            result = rx_engine_feed(rx_opcode, &activity);
            
            if (result){
                rx_fps[cnt] += 1;
                usleep(3000);  //XXX:::
             }
         }
         
        } while (rx_opcode > 0);  // while there are bytes to read
     
                        
     if (opt_debug) {
          gettimeofday(&fps_tim2, NULL);
          if (fps_tim2.tv_sec >= fps_tim1.tv_sec + 1){
              gettimeofday(&fps_tim1, NULL);
              for (cnt2 = 0; cnt2 < XLR_LAST+1 ; cnt2++){ 
                  if (rx_fps[cnt2]) printf("%d: RX(%d): %d Slots @ %d FPS\n",(int)fps_tim2.tv_sec, cnt2, result, rx_fps[cnt2]);          
                  if (rx_eng[cnt].state != RX_STATE_OFF){                    
                  }           
                 rx_fps[cnt2] = 0;
              } // for-loop
        } // every second
       } // if debug  
   
      } // if rx_eng not RX_STATE_OFF 
   //   sched_yield();
    } // for each uart
 
    if (!ti)       // no activity on any ports 
         usleep(2000);
    
   } // loop forever
    
} // rx_engine










// ============================================
// ============================================
// ============= Transmission ======================
// ============================================
// ============================================


// tx a frame to the wire
// takes an xuart_fd, and pointer to a tx_frame_buf
// assumes xuart_fd is open
// return number of bytes written
static inline int tx_frame(int xuart_fd, struct tx_frame_t *frame) {
  int res = TEMP_FAILURE_RETRY(
                    write(xuart_fd,(uint8_t *)&frame->data[0],
                             (frame->length)*sizeof(uint16_t) )
                   ); 
  return res;
}


// prepare a frame for transmission to the wire
// expect XLR mutex is already locked
// param frame is the frame to build for the XUART  (tx_frame_buf)
// break_tim and mab_tim specifiy the packet timing (in lots of 4uS)
// length is the number of slots to send
// @dmx_slot_data is a pointer to an array of unsigned short dmx slot data. (dmx_tx_buf)
// return <=0 if failed, otherwise tx_dmx_frame length (including preamble)
static inline int init_tx_frame(struct tx_frame_t *frame, int xlr_num, int break_tim, int mab_tim, int length, uint8_t *dmx_slot_data ) {
    int cnt = 0;
    int cnt2 = 0; // tx irq interval occurance counter
    uint8_t datas = 0;
    if ((length > MAX_DMX_FRAME_SIZE) || (length < 0)) return -1; // check for sanity

    frame->length = PREAMBLE_SIZE + length;
    frame->frame_time = break_tim + mab_tim + (44*length);  // the frame duration in uS
#ifdef DEBUG                   
    if (opt_debugv) printf("tx_frame_time = %d\n",frame->frame_time);   
#endif

#ifdef BREAK_SYNC
    int bdelay = 0;
/*
    // delay to sync breaks

    if (xlr_num == 0) {
     bdelay = ( 3* ((2*break_tim / BIT_TIME) + (2*mab_tim / BIT_TIME) + (12 *BIT_TIME)) );
    } else {
     bdelay = (XLR_LAST-xlr_num) * (  -((XLR_LAST-xlr_num)*44/BIT_TIME) + ((xlr_num==3)*-2) +  ((xlr_num==1) * (44/BIT_TIME))  +  (2*break_tim / BIT_TIME) + (2*mab_tim / BIT_TIME) + (15 *BIT_TIME)) ;
    }
    fprintf(stdout,"ch_time[%d] = %d\n",xlr_num, bdelay);
*/

    switch (xlr_num){
     case 0:  bdelay = 426; break;
     case 1:  bdelay = 282; break;
     case 2:  bdelay = 141; break;
     case 3:  bdelay = 0; break;
     default: bdelay = 0;
    }     
    
    frame->data[0] = ch_idle(bdelay); // delay universes to syncronize
    
    frame->data[1] = ch_break(break_tim / BIT_TIME); // break
    frame->data[2] = ch_idle(mab_tim / BIT_TIME);    // mark after break
#endif

 #ifndef BREAK_SYNC
    // no frame sync
    frame->data[0] = ch_break(break_tim / BIT_TIME); // break
    frame->data[1] = ch_idle(mab_tim / BIT_TIME);    // mark after break
 #endif
 
    while (cnt != (frame->length - PREAMBLE_SIZE)) {      // TODO: check
     cnt2++;
     datas = (uint8_t)*(dmx_slot_data+cnt); // get the data byte
     frame->data[PREAMBLE_SIZE+cnt] = ch_data(datas,(cnt2 == TX_IRQ_INTERVAL)); // encode it to xuart data 
     cnt++; 
     if (cnt2 == TX_IRQ_INTERVAL) 
        cnt2 = 0;
    }           
    return frame->length;
}




// this thread sends DMX or RDM on XLR's that are in a TX mode
void *tx_engine(void *thread_id){  
  int cnt;
  int tx_fps = 0;
  int cnt_tx = 0;
  int longest_frame_duration = 0;
  struct timeval fps_tim1;  
  struct timeval fps_tim2;  
  
  gettimeofday(&fps_tim1, NULL);
 
  if (opt_debugv)
    printf("%d: TX engine commenced.\n",(int)fps_tim1.tv_sec);   
           
  do {
        cnt_tx = 0; 
        longest_frame_duration = 10000;  // (less than half the time of a real frame)
        pthread_mutex_lock(&xlr_lock);
        for (cnt = XLR_FIRST; cnt < XLR_LAST+1; cnt++) {
            if (xlr[cnt].fd && (xlr[cnt].mode >= MODE_TX)){
#ifdef TX_PRIO_RT            
                 raise_RT_priority(xuartctl_pid);    // boost to RT priority
#endif
                 tx_frame(xlr[cnt].fd, &xlr[cnt].tx_frame_buf);  // send the buffer for this XLR
              //   if (opt_debugv) printf("tx_frame\n");
                 if (opt_debugv) cnt_tx++;
                 if (xlr[cnt].mode == MODE_TX_RDM) {
//TODO:        mode = MODE_RX_RDM; // return to listening mode after RDM TX
                  } else {
                    if (xlr[cnt].tx_frame_buf.frame_time > longest_frame_duration)
                        longest_frame_duration = xlr[cnt].tx_frame_buf.frame_time;
                  }               
#ifdef TX_PRIO_RT
                 drop_RT_priority(xuartctl_pid);       // back to previous priority
#endif
            } // if mode_tx
        } // for-loop
        pthread_mutex_unlock(&xlr_lock);   
        sched_yield();
     
        if ((opt_debugv) && (cnt_tx != 0)) {   // fps calculations
             tx_fps += 1;
             gettimeofday(&fps_tim2, NULL);
             if (fps_tim2.tv_sec >= fps_tim1.tv_sec + 1) {
                 gettimeofday(&fps_tim1, NULL);
                 printf("%i:  using tx_ports: %d  -  tx_fps: %d\n",(int)fps_tim1.tv_sec, cnt_tx, tx_fps);                     
                 tx_fps=0;
                 cnt_tx = 0;
             }
        } // if debugv
        
       // sleep for the length of longest frame duration until we send again
       usleep(longest_frame_duration);
       
         
  } while (1!=0);
  
} // tx engine








#ifdef HIP_SHM
// ============================================
// ============================================
// =========== Shared Memory =======================
// ============================================
// ============================================

// create HipNet TX, RX shared memory segments
// return > 0 on success, or various negative values when fails
int shm_start (void) {

int cnt;

//  --------  TX
//  tx segments are data sent to XLR's
 // create tx shmem segments for every xlr
 for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 

    tx_shmkey[cnt] = 44779 + cnt;
    
    // create the segment.
    if ((tx_shmid[cnt] = shmget(tx_shmkey[cnt], sizeof(shm_block_t), IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    // attach to the segment
    if ( (tx_shm[cnt] = shmat(tx_shmid[cnt], NULL, 0)) == (void *) -1 ) {
        perror("shmat");
        exit(1);
    }
      
  // initialize the block
  memset(tx_shm[cnt], 0, sizeof(shm_block_t));
  tx_shm[cnt]->data.head = htonl((uint32_t) HIPNET_DMX);
  tx_shm[cnt]->seq = 0;
  
 } // for each tx shmem segment xlr

//  --------  RX
//  rx segments are data received from XLR's
 // create rx shmem segments for every xlr
 for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 

    rx_shmkey[cnt] = 44779 + (XLR_LAST+1) + cnt;
    
    // create the segment.
    if ((rx_shmid[cnt] = shmget(rx_shmkey[cnt], sizeof(shm_block_t), IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    // attach to the segment
    if ( (rx_shm[cnt] = shmat(rx_shmid[cnt], NULL, 0)) == (void *) -1 ) {
        perror("shmat");
        exit(1);
    }
      
    // initialize the block
    memset(rx_shm[cnt], 0, sizeof(shm_block_t));
    rx_shm[cnt]->data.head = htonl((uint32_t) HIPNET_DMX);
    rx_shm[cnt]->seq = 0;
  
   } // for each rx shmem segment xlr 
return 1;    
}

 

// poll the shm blocks for data from a controller, process if received and return 1 if ok.
// otherwise return 0
// expects shm_start() has been called
int shm_run (void) {
    static int last_seq[XLR_LAST+1];

    int cnt;
    for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 
        // check for a new frame in tx_shm block
        if (tx_shm[cnt]->seq != last_seq[cnt] && tx_shm[cnt]->ack) {
          //while (!tx_shm[cnt]->ack) {
            // wait for ack to be 1
            // TODO: timeout...
          // }
          last_seq[cnt] = tx_shm[cnt]->seq; 

          // read new dmx for this universe
          process_packet(&tx_shm[cnt]->data);
          
          tx_shm[cnt]->ack = 0;  // ack the reception data        
        } // if new frame
    } // for every xlr
 return 1;
}


// write hipnet shm frames out when we RX a frame of dmx from an xlr    
// expects xlr number, an hipnet packet and it's size
static inline int hipnet_shm_write(int xlr_num, unsigned short *data, int size) {
 while (rx_shm[xlr_num]->ack) { /* TODO:  timeout */ }
   memcpy(&rx_shm[xlr_num]->data, data, size);
   rx_shm[xlr_num]->seq += 1;
   rx_shm[xlr_num]->ack = 1;
 return 1;
 
}

#endif 
// -------- END of SHM --------



// ============================================
// ============================================
// =========== HIP-NET Socket =================
// ============================================
// ============================================

// create HipNet socket for sending/receiving datagrams
// return > 0 on success, or various negative values when fails
int network_start (void) {
  
   hipnet_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
   if ( hipnet_sock < 0) {  
     printf("Failed to create HipNet socket!\n");
     return -20;
     }
     
   memset(&sockAddr_hipnet, 0, sizeof(sockAddr_hipnet));
   sockAddr_hipnet.sin_family = AF_INET;
   sockAddr_hipnet.sin_addr.s_addr = inet_addr("127.0.0.1"); //INADDR_ANY;
   sockAddr_hipnet.sin_port = htons(HIPNET_PORT);
   
   int reuse = 1;
   if(setsockopt(hipnet_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)  {
     printf("Cannot set HipNet socket to SO_REUSEADDR!\n");
     return -24;
    } 

   int broad = 1;
   if(setsockopt(hipnet_sock, SOL_SOCKET, SO_BROADCAST, (char *)&broad, sizeof(broad)) < 0)  {
     printf("Cannot set HipNet socket to SO_BROADCAST!\n");
     return -23;
    } 

   int result = bind(hipnet_sock, (struct sockaddr *) &sockAddr_hipnet,sizeof(sockAddr_hipnet));
   if (result) {
       printf("Cannot bind HipNet socket to port number %d:  %d\n.", HIPNET_PORT, result);
       return -21;
    }

    int non_blocking = 1;
    if (fcntl(hipnet_sock, F_SETFL, O_NONBLOCK, non_blocking) == -1 ) {
        printf("Failed to set HipNet socket to non-blocking.\n");
        return -22;
    }
  
  // initialize our packet buffer
  memset(&outpacket, 0, sizeof(outpacket));
  outpacket.head = htonl((uint32_t) HIPNET_DMX );

return 1;    
}



// poll the network for incomming packets, process if received and return 1.
// otherwise return 0
// expects HipNet socket is open
int network_run (void) {

        static unsigned int maximum_packet_size = sizeof(hipnet_data_t);        
        static socklen_t fromLength = sizeof( sockFrom_hipnet );
        
        // grab straight into the packet buffer
        int rx_bytes;
        rx_bytes = recvfrom(hipnet_sock, (uint8_t*)&packet, maximum_packet_size,  0,
                                         (struct sockaddr*)&sockFrom_hipnet, &fromLength);

        if ( rx_bytes <= 0)
            return 0;  // nothing received

        unsigned int from_address = ntohl(sockFrom_hipnet.sin_addr.s_addr);
        unsigned int our_address = ntohl(sockAddr_hipnet.sin_addr.s_addr);
  
        // TODO: could check for header here early on, to see if even a hipnet packet...
  
       if (from_address != our_address) {
           if (opt_debugx) printf("HipNet> warning, non-loopback addressed packet detected!");  
       }
    
      process_packet(&packet);   // process the packet       
       
       return 1;
}

// broadcast a datagram on the HipNet socket    
static inline int hipnet_socket_write(unsigned short *data, int size) {
   return sendto(hipnet_sock, data, size, 0, (struct sockaddr *)&sockAddr_hipnet, sizeof(sockAddr_hipnet)) ;   
}



// ------  process HipNet packets/data
int process_packet(hipnet_data_t *packet){
 
        // only listen to controller packets, srcnode type 1.   
       if (packet->srcnode != 0 ) {
         if (opt_debugx) printf("HipNet> ignoring non-controller packet.\n");
         return 0;
       }
   
        // only listen to our universes  
       if (packet->universe > XLR_LAST)
           return 0;
   
       // check the header of the packet 
       switch (htonl((uint32_t)packet->head)) {
         case HIPNET_PING:       // sent by controller
            if (opt_debugv) { printf("HipNet> Ping received.\n"); }
            break;
         case HIPNET_PONG:    // sent by nodes
            if (opt_debugv) { printf("HipNet> Pong received.\n"); }
            break;
         case HIPNET_DMX:     // sent by both
#ifdef DEBUG                   
            if (opt_debugv) 
                printf("HipNet> %d bytes arrived for XLR %d," 
                            "START=%d ch1=%d ch2=%d ch3=%d\n",
                            ntohs(packet->size), 
                            packet->universe,packet->data[0],packet->data[1],packet->data[2],packet->data[3] );
#endif   
            pthread_mutex_lock(&xlr_lock);
            
            int i = packet->universe;
            // packet mode asks us to enter 'send dmx' mode and send attached data...
            // so we prepare the XLR with the data for transmission        
            if ((packet->mode == 0x00) && (packet->size != 0)) {
                init_tx_frame(&xlr[i].tx_frame_buf, i,xlr[i].break_time,xlr[i].mab_time,ntohs(packet->size)+1,(uint8_t*)&packet->data);
                if (xlr[i].mode != MODE_TX) { 
                  if (opt_debug) { printf("HipNet> Set XLR %d to MODE_TX\n",packet->universe); }
                  xlr[i].mode = MODE_TX;
                }  
            } else                         
            if (packet->mode == 0x01) {
               xlr[i].mode = MODE_DISABLE;
               //TODO: lock rx_eng and enable
               if (opt_debug) { printf("HipNet> Set XLR %d to MODE_RX\n",packet->universe); }
            } else 
            if (packet->mode == 0x0F) {
               xlr[i].mode = MODE_DISABLE;
               if (opt_debug) { printf("HipNet> XLR %d disabled.\n",packet->universe); }
            }    
           
            pthread_mutex_unlock(&xlr_lock);

            break;
         default:
            if (opt_debug) { printf("HipNet> corrupt packet received.\n"); }
       } // switch
       
return 1;
}





// ============================================
// ============================================
// =============== INIT ==========================
// ============================================
// ============================================


// initialization of dmx and XUART buffers  
// return 1 on success
int initialize_buffers(void) {
  int i, cnt;
  if (opt_debugv) printf("init buffers... ");

 for (cnt = XLR_FIRST ; cnt < XLR_LAST+1; cnt++) { 
  xlr[cnt].fd = -1; 
  xlr[cnt].mode = MODE_DISABLE;  // XXX:  should default to disabled mode on startup
 
  // used for TX
  xlr[cnt].break_time = 176;  // == 176us -- these times should be nicely divisible by 4us
  xlr[cnt].mab_time = 12;     // == 12us
  xlr[cnt].inter_frame_delay = 176; // == 176us to wait between packets

  for (i=0;i<PREAMBLE_SIZE+MAX_DMX_FRAME_SIZE+1;i++) 
         xlr[cnt].tx_frame_buf.data[i] = ch_data(0,0);
  xlr[cnt].tx_frame_buf.length = 0;
  xlr[cnt].tx_frame_buf.frame_time = 0;

  for (i=0;i<MAX_DMX_FRAME_SIZE+1;i++) xlr[cnt].dmx_tx_buf[i] = packet.data[i] = 0;
   
  packet.size = 513;
 
  init_tx_frame(&xlr[cnt].tx_frame_buf,cnt, xlr[cnt].break_time,xlr[cnt].mab_time,packet.size,&packet.data[0]);
 }
  
 if (opt_debugv) printf("done.\n");
  
 return 1; 
}






// ============================================
// ============================================
// =============== MAIN =========================
// ============================================
// ============================================

int main(int argc, char **argv) {

  int network_status = -1;
  int i = 0; 

  struct option2 opts[] = {
#ifdef HIP_SHM
    {0, (opt_func)opt_bool, &opt_shm, "<s>hm",   " use shared memory for HipNet instead of sockets." },
#endif
    {0, (opt_func)opt_bool, &opt_background, "<b>ackground",   " - daemonize." },
    {0, (opt_func)opt_bool, &opt_tx, "<t>ransmit",   " start all ports in tx-mode. (default is disabled)" },
    {0, (opt_func)opt_bool, &opt_rx, "<r>eceive",   " start all ports in rx-mode. (default is disabled)" },
    {0, (opt_func)opt_bool, &opt_2node, "<2>node" ,   " start XLR 0 as output, XLR 1 as input" },
    {0, (opt_func)opt_bool, &opt_debug, "<d>ebug",   " messages, do not daemonize." },
    {0, (opt_func)opt_bool, &opt_debugv, "<v>erbose",   " debugging messages." },
    {0, (opt_func)opt_bool, &opt_debugx, "e<x>tra",   " debug RX opcode messages." },
    {0, (opt_func)opt_bool, &opt_debug_log, "<l>og",   " debug messages to file /tmp/hipdmxctl.log" },
    {0,0,0,"HipNet XLR engine.\n\n","" }
  };

   i = find_xuartctl();
   if (i <= 0) {
    printf("Unable to locate xuartctl server process, exiting!\n");   
    return -1;
   } else { xuartctl_pid = i; }
  
// prepare our buffers used for dmx and uart operations
   initialize_buffers();

// process command line options, open specified device or socket
   process_options(argc,argv,opts);

   if (opt_debug_log)  {    log_setup();   } // setup logging to file
   
   if (opt_debug) {
      printf("HipNet protocol revision as at: " __TIME__ "-" __DATE__ "\n");
      printf("Supporting %d XLR ports\n",XLR_LAST+1);
#ifdef TX_PRIO_RT
      printf("Compiled with priority support.\n");
#endif
#ifdef HIP_SHM
      printf("Compiled with shared memory support.\n");
#endif
#ifdef BREAK_SYNC
      printf("Compiled with break syncronization support.\n");
#endif
   }

#ifdef TX_PRIO_RT
   if (opt_debugv) printf("Setting up scheduler... ");  
   init_sched(xuartctl_pid);    // prepare our scheduled 
   if (opt_debugv) printf("done!\n");  
#endif

   // XUART initialization
   if (opt_debugv) printf("Connecting to XLRs...\n");  
 
   xlr[XLR_FIRST+0].fd = xuart_connect("127.0.0.1:7351", "--mode=9n1,raw,rlw=128 --speed=250000");   
   xlr[XLR_FIRST+1].fd = xuart_connect("127.0.0.1:7352", "--mode=9n1,raw,rlw=128 --speed=250000");   
   xlr[XLR_FIRST+2].fd = xuart_connect("127.0.0.1:7355", "--mode=9n1,raw,rlw=128 --speed=250000");   
   xlr[XLR_FIRST+3].fd = xuart_connect("127.0.0.1:7356", "--mode=9n1,raw,rlw=128 --speed=250000");   

   for (i = XLR_FIRST; i < XLR_LAST+1; i++) {
 	    if (!xlr[i].fd) {
    	    printf("Failed to open XLR %d\n",i);
    	} 
    	else {
            if (opt_tx)  
                xlr[i].mode = MODE_TX;
        }
   }

    // initialize the RX engines
    rx_init_engines();

    // warm the glow plugs
    if (opt_rx) {
       for (i = XLR_FIRST; i < XLR_LAST+1; i++)
            rx_reset_engine(i); // resets & starts RX mode for the XLR
    }    
      
   // start only two ports, 0 is out & 1 is in
   if (opt_2node) {
       for (i = XLR_FIRST; i < XLR_LAST+1; i++) { rx_eng[i].state = RX_STATE_OFF; }
       for (i = XLR_FIRST; i < XLR_LAST+1; i++) { xlr[i].mode = MODE_DISABLE; }
       
       xlr[0].mode = MODE_TX;
       rx_reset_engine(1);
       
   }   
   
   
// -------  Threading
   pthread_mutex_init(&xlr_lock, NULL);   

// create TX thread
   long tid1 = 0;
   int rc1 = pthread_create(&xlr_tx_thread, NULL, tx_engine, (void *)tid1 );
   if (rc1){
       printf("Fatal: pthread_create(TX) failed: %d\n", rc1);
       return -1;
   }
 
// kick over the RX engine
   long tid2 = 0;
   int rc2 = pthread_create(&xlr_rx_thread, NULL, rx_engine, (void *)tid2 );
   if (rc2){
       printf("Fatal: pthread_create for rx_engine failed: %d\n", rc2);
       return -1;
   }

#ifdef HIP_SHM 
   if (!opt_shm) {
#endif       
    if (opt_debug) printf("Using Sockets for HipNet communication.\n");
    network_status = network_start();  // start the HipNet network via sockets
#ifdef HIP_SHM 
   } else { 
    if (opt_debug) printf("Using SHM for HipNet communication.\n");
    network_status = shm_start();  // use shm instead of sockets
   }
#endif

   if (network_status <= 0) {
    printf("Fatal: Failed to start network!\n");
    return -1;
   } 

   
   // TODO: implement HUP signal
   signal(SIGPIPE,SIG_IGN); // ignore this annoying signal
 
   if (opt_debug) { 
    printf("\nHipNet services commence...\n"); 
    //gettimeofday(&fps_tim1, NULL);
   }
   
   if (opt_background) {
     if (opt_debug) printf("Daemonizing...\n");
     daemon(1,0);
   }

   // loop forever
   while (1!=0) {
                                                      
                                                    
// ----- Main thread handles HipNet
#ifdef HIP_SHM
     if (!opt_shm) {
#endif      
      network_status = network_run();
#ifdef HIP_SHM
     } else {
      network_status = shm_run();
     }    
#endif

     if (network_status <= -1) {
        printf("Network run failed! (%d)\n",network_status);
        return -1;
     }
      
      usleep(1000);                

      if (opt_debug_log) {
        static int flush_cnt = 0;
        flush_cnt++;
        if (flush_cnt > 10) { fflush(logfile); flush_cnt = 0; }
      }
     
    }  // forever loop


    if (opt_debug_log){
      fclose(logfile);
    }

  return 0;  // never get here
}

